<template>
    <a-spin :spinning="spinning" :tip="spinningTip">
    <div class="PageContainer" ref="PageContainerRef" :class="{ isMobile: isMobile, bg1: setting.bg == 1, bg2: setting.bg == 2, bg3: setting.bg == 3 }">

        <div class="header df aic jcse">
            <div class="df aic">
                <img class="logo link" src="@img/KODUCKYA.png" alt="KODUCKYA" @click="link('index')">
                <div class="link" @click="link('index', Language)">{{ $t('index.Home') }}</div>
                <div class="link" @click="link('shop', Language)">{{ $t('index.Shop') }}</div>
                <div class="link" @click="link('custom', Language)">{{ $t('index.Custom') }}</div>
                <div class="link" @click="link('contact', Language)">{{ $t('index.Contact') }}</div>
            </div>
            <div class="df aic">
                <a-popover trigger="click">
                    <template #content>
                        <div class="languageButtons">
                            <div class="languageButton df aic" @click="changeLanguage('jp')">
                                <svg v-if="Language == 'jp'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="select"><path d="M20 6 9 17l-5-5"/></svg>
                                {{ $t('index.LanguageJp') }}
                            </div>
                            <div class="languageButton df aic" @click="changeLanguage('en')">
                                <svg v-if="Language == 'en'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="select"><path d="M20 6 9 17l-5-5"/></svg>
                                {{ $t('index.LanguageUs') }}
                            </div>
                        </div>
                    </template>
                    <div class="language df aic">
                        <div class="text">{{ $t('index.languageButton') + ' | USD $' }}</div>
                        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="downIcon ml"><path d="m6 9 6 6 6-6"/></svg>
                    </div>
                </a-popover>
                <img class="icon" src="@img/order.png" alt="order" @click="link('order')">
                <!-- <img class="icon" src="@img/shop.png" alt="order"> -->
            </div>
        </div>

        <template v-if="isCustom">
            <div class="titleBar">
                <div class="title">{{ $t('index.Pick_your_favorite') }}</div>
                <div class="text">{{ $t('index.text1') }}</div>
                <div class="setting fc">
                    <SettingOutlined :style="{ fontSize: '3vmin', color: '#86868b' }" @click="handleSetting" />
                </div>
            </div>

            <div class="df aic styleList mb">
                <div class="styleListItem fc mr" v-for="(item, index) in from.list" :key="index" :class="{'active': index === from.targetIndex }" @click="handleClickStyle(index)">{{ $t('index.STYLE') + ' ' + (index + 1) }}</div>
            </div>

            <div class="mainContainer" :class="{ df: !isMobile, jcse: !isMobile, isMobile: isMobile }">
                <div ref="canvasContainer" class="canvasWrapper" :class="{ isMobile: isMobile}"></div>
                <div class="canvasRight">
                    <div class="item">
                        <div class="itemTitle">{{ $t('index.SURFACE_FINISH') }}</div>
                        <div class="df aic">
                            <div class="selectItem fc" :class="{'select': targetItem.light === 1 }" @click="handleChangeFrom('light', 1)">{{ $t('index.Polished') }}</div>
                            <div class="selectItem fc" :class="{'select': targetItem.light === 2 }" @click="handleChangeFrom('light', 2)">{{ $t('index.Matte') }}</div>
                        </div>
                    </div>
                    <div class="item">
                        <div class="itemTitle">{{ $t('index.EMBELLISHED') }}</div>
                        <div class="df aic">
                            <div class="selectItem fc" :class="{'select': targetItem.zuan === 1 }" @click="handleChangeFrom('zuan', 1)">
                                {{ $t('index.With_rhinestones') }}
                            </div>
                            <div class="selectItem fc" :class="{'select': targetItem.zuan === 0 }" @click="handleChangeFrom('zuan', 0)">
                                {{ $t('index.Plain') }}
                            </div>
                        </div>
                    </div>
                    <!-- 粉粒选择 暂时去掉 默认大粉粒 -->
                    <!-- <div class="item">
                        <div class="itemTitle">PARTICLE</div>
                        <div class="df aic">
                            <div class="selectItem fc" :class="{'select': targetItem.fen === 1 }" @click="handleChangeFrom('fen', 1)">Small</div>
                            <div class="selectItem fc" :class="{'select': targetItem.fen === 2 }" @click="handleChangeFrom('fen', 2)">Large</div>
                        </div>
                    </div> -->
                    <div class="item">
                        <div class="itemTitle">{{ $t('index.COLOR') }}</div>
                        <div class="df aic">
                            <div class="selectItem fc" :class="{'select': targetItem.color === 1 }" @click="handleChangeFrom('color', 1)">
                                {{ $t('index.Single_Color') }}
                            </div>
                            <div class="selectItem fc" :class="{'select': targetItem.color === 2 }" @click="handleChangeFrom('color', 2)">
                                {{ $t('index.Two_Colors') }}
                            </div>
                        </div>
                        <div class="selectItem fc" :class="{'select': targetItem.color === 3 }" @click="handleChangeFrom('color', 3)">
                            {{ $t('index.Three_Colors') }}
                        </div>
                    </div>
                    <div class="item df aic">
                        <div class="mr">
                            <colorBar @colorChange="(e) => handleColorChange(e, 1)" @colorSelect="(e) => handleColorSelect(e, 1)" :targetColor="targetItem.colorItem1" :isSelected="targetItem.isSelected1" :isMobile="isMobile"></colorBar>
                            <colorBar v-if="targetItem.color > 1" @colorChange="(e) => handleColorChange(e, 2)" @colorSelect="(e) => handleColorSelect(e, 2)" :targetColor="targetItem.colorItem2" :isSelected="targetItem.isSelected2" mt :isMobile="isMobile"></colorBar>
                            <colorBar v-if="targetItem.color > 2" @colorChange="(e) => handleColorChange(e, 3)" @colorSelect="(e) => handleColorSelect(e, 3)" :targetColor="targetItem.colorItem3" :isSelected="targetItem.isSelected3" mt :isMobile="isMobile"></colorBar>
                        </div>
                        <!-- <ColorPicker ref="ColorPickerRef" v-model="colorParams" @colorChanged="handleChangeColorPicker" size="large"></ColorPicker> -->
                    </div>
                    <div class="item">
                        <!-- <div class="itemTitle">Quantity</div> -->
                        <!-- <a-input-number class="selectItem" v-model:value="from.list[from.targetIndex].number" :min="1" :max="9999" :precision="0"  /> -->
                        <div class="itemTip">· {{ $t('index.text2') }}</div>
                        <div class="itemTip">· {{ $t('index.text3') }}</div>
                        <div class="itemTip">· {{ $t('index.text4') }}</div>
                        <div class="itemTip">· {{ $t('index.text5') }}</div>
                    </div>
                    <div class="item df aic buttons">
                        <div v-if="from.list.length > 1" class="button delete mr fc" @click="handleRemoveStyleItem">{{ $t('index.text6') }}</div>
                        <div v-if="from.list.length < 10" class="button fc" @click="handleAddStyleItem">{{ $t('index.text7') }}</div>
                    </div>
                    <div class="item fc submit" @click="submit">{{ $t('index.text8') }}</div>
                    <div class="item">
                        <div class="itemTip">{{ $t('index.text9') }}</div>
                    </div>
                </div>
                <!-- 操作提示 -->
                <div v-if="!isMobile" class="controls-hint fc">
                    Drag: Rotate | Mouse Wheel: Zoom
                </div>
                <div v-if="isMobile" style="height: 20vmax;"></div>
            </div>
        </template>

        <template v-else>
            <div class="titleBar">
                <div class="title">{{ $t('index.Pre-order') }}<</div>
                <div class="text">{{ $t('index.text10') }}</div>
            </div>
            <div class="orderItem df aic" v-for="(orderItem, orderIndex) in orderList" :key="orderIndex">
                <div class="left">
                    <img :src="orderItem.img">
                </div>
                <div class="right">
                    <div class="itemTitle">{{ $t('index.STYLE') }}</div>
                    <div class="df aic">
                        <div class="selectItem fc">{{ orderItem.style.light === 1 ? $t('index.Polished') : $t('index.Matte') }}</div>
                        <div class="selectItem fc">{{ orderItem.style.zuan === 1 ? $t('index.With_rhinestones') : $t('index.Plain') }}</div>
                    </div>
                    <div class="df aic">
                        <!-- 粉粒默认大粉粒 不显示 -->
                        <!-- <div class="selectItem fc">{{ orderItem.style.fen === 1 ? 'Small' : 'Large' }}</div> -->
                        <div class="selectItem fc">{{ orderItem.style.color === 1 ? $t('index.Single_Color') : orderItem.style.color === 2 ? $t('index.Two_Colors') : $t('index.Three_Colors') }}</div>
                    </div>
                    <div v-if="orderList && orderList.length > 1" class="selectItem fc delete" style="margin-top: 3vmin;" @click="deleteOrderItem(index)">{{ $t('index.text6') }}</div>
                </div>
            </div>
            <div class="orderFrom">
                <div class="title">{{ $t('index.Email') }}</div>
                <div class="inputBox">
                    <input v-model="orderFrom.email" type="text" :placeholder="$t('index.text11')">
                </div>
                <div class="title">{{ $t('index.Phone') }}</div>
                <div class="inputBox">
                    <input v-model="orderFrom.phone" type="text" :placeholder="$t('index.text12')">
                </div>
                <div class="title">{{ $t('index.Name') }}</div>
                <div class="inputBox">
                    <input v-model="orderFrom.name" type="text" :placeholder="$t('index.text13')">
                </div>
                <div class="title">{{ $t('index.Remark') }}</div>
                <div class="inputBox">
                    <input v-model="orderFrom.remark" type="text" :placeholder="$t('index.text14')">
                </div>
            </div>
            <div class="footer fc">
                <div class="button fc" @click="submitOrder">{{ $t('index.Submit') }}</div>
            </div>
            <div v-if="isMobile" style="height: 20vmax;"></div>
        </template>
    </div>

    <a-modal v-model:open="openSuccessModal" title="success" :footer="null" :closable="false" :maskClosable="false">
        <a-result
            status="success"
            :title="$t('index.text15')"
            :sub-title="$t('index.text16')"
        >
            <template #extra>
                <a-button key="console" type="primary" @click="link('index')">{{ $t('index.back') }}</a-button>
            </template>
        </a-result>
    </a-modal>

    <a-drawer
        v-model:open="showSetting"
        :root-style="{ color: 'blue' }"
        :title="$t('index.Setting')"
        placement="right"
        width="60vmin"
    >
        <div class="settingModal">
            <div class="itemTitle">{{ $t('index.BACKGROUND') }}</div>
            <div class="df aic">
                <div class="selectColor" style="background: #fff;" @click="handleChangeSetting('bg', 1)"></div>
                <div class="selectColor" style="background: #121212;" @click="handleChangeSetting('bg', 2)"></div>
            </div>
        </div>
    </a-drawer>
    </a-spin>
</template>

<script setup>
import { ref, onMounted, onUnmounted, computed } from 'vue';
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { SimplifyModifier } from 'three/addons/modifiers/SimplifyModifier.js';
import colorBar from '@/components/colorBar.vue';
import { colorEvent } from 'v3-color-picker'

import ColorPicker from "@mcistudio/vue-colorpicker";
import "@mcistudio/vue-colorpicker/dist/style.css";
import { addGlitterToCase, toggleGlitterSize } from "@/utils/Glitter"
import { captureModelScreenshot, setupScreenshotWithZoom, getCurrentViewConfig, setupAutoRotate, ZoomController, link, scrollTo } from "@/utils/utils" 
import { message } from 'ant-design-vue';
import fetch from "@/api/fetch.js"
import { i18n, changeLanguage } from '../utils/i18n'
import {
    SettingOutlined
} from '@ant-design/icons-vue';

const Language = computed(() => {
    return i18n.global.locale
})

const spinning = ref(false)
const spinningTip = ref('Loading...')
const from = ref({
    list: [
        {
            light: 1, // 1: 亮光 2: 哑光
            color: 2, // 1: 单色 2: 双色 3: 三色
            zuan: 1, // 1: 带钻 0: 无钻
            fen: 2, // 1: 小 2: 大
            colorItem1: 'rgb(151, 97, 233)',
            colorItem2: 'rgb(64, 208, 233)',
            colorItem3: 'rgb(239, 15, 15)',
            isSelected1: false,
            isSelected2: false,
            isSelected3: false,
            number: 1,
        }
    ],
    targetIndex: 0,
})
const isCustom = ref(true) // 当前为自定义界面
const openSuccessModal = ref(false) // 提交成功弹窗
const orderList = ref([]) // 订单列表
const orderFrom = ref({
    email: '',
    phone: '',
    name: '',
    remark: '',
})

let testOrderPage = false // 测试用
if (testOrderPage) {
    console.log('testOrderPage 读取订单列表')
    isCustom.value = false
    let list = localStorage.getItem('orderList')
    if (list) {
        orderList.value = JSON.parse(list)
        console.log('orderList.value',orderList.value)
    }
}

// 移动端适配(手机平板)
let isMobile = ref(false)
// 根据屏幕尺寸比例匹配
const matchMobile = () => {
    let width = document.body.clientWidth
    let height = document.body.clientHeight
    if (width / height < 1.2) isMobile.value = true
    else isMobile.value = false
    console.log('matchMobile isMobile.value', isMobile.value)
}
matchMobile()

const handleChangeFrom = (key, value) => {
    if (from.value.list[from.value.targetIndex][key] == value) return
    from.value.list[from.value.targetIndex][key] = value
    if (key == 'light') handleChangeSheen() // 亮度改变
    if (key == 'zuan') toggleDiamonds() // 镶钻改变
    if (key == 'fen') toggleGlitterSize() // 粉粒大小改变
    if (key == 'color') handleChangeColorNumber() // 颜色改变
}

const colorParams = ref({
    mode: 'solid',
    color: {
        r: 239,
        g: 15,
        b: 15,
        a: 1
    }
})

const targetItem = computed(() => from.value.list[from.value.targetIndex])

const containerGradientStr = computed(() => {
    if (targetItem.value.color == 1) return `linear-gradient(to bottom, ${targetItem.value.colorItem1} 0%, ${targetItem.value.colorItem1} 100%)`
    else if (targetItem.value.color == 2) return `linear-gradient(to bottom, ${targetItem.value.colorItem1} 0%, ${targetItem.value.colorItem2} 100%)`
    else if (targetItem.value.color == 3) return `linear-gradient(to bottom, ${targetItem.value.colorItem1} 0%, ${targetItem.value.colorItem2} 50%, ${targetItem.value.colorItem3} 100%)`
})

// 拆开rgb(239, 15, 15)为r:239、g:68、b:68对象
const rgbToRgba = (rgb, log) => {
    let r = rgb.match(/\d+/g)[0]
    let g = rgb.match(/\d+/g)[1]
    let b = rgb.match(/\d+/g)[2]
    return { r: Number(r), g: Number(g), b: Number(b), a: 1 }
}

// 同步更新colorParams(渐变选择器的参数)
const updateColorParams = () => {
    let result = {}
    console.log('updateColorParams')
    let mode = targetItem.value.color == 1 ? 'solid' : 'linear'
    result.mode = mode
    if (mode == 'solid') result.color = rgbToRgba(targetItem.value.colorItem1, 1)
    else {
        result.gradients = []
        result.degree = 0
        for (let index = 0; index < targetItem.value.color; index++) result.gradients.push({ percent: 0, color: { r: 0, g: 0, b: 0, a: 1 }})
        result.gradients[0].color = rgbToRgba(targetItem.value.colorItem1,2)
        if (targetItem.value.color >= 2) result.gradients[1].color = rgbToRgba(targetItem.value.colorItem2,3)
        if (targetItem.value.color == 3) result.gradients[2].color = rgbToRgba(targetItem.value.colorItem3,4)
    }
    console.log('updateColorParams colorParams', result)
    colorParams.value = result
}

const handleColorChange = (e, index) => {
    from.value.list[from.value.targetIndex][`colorItem${index}`] = e
    updateModelGradient(containerGradientStr.value)
    console.log('handleColorChange e index', e, index)
    updateColorParams()
}

const handleChangeColorNumber = () => {
    updateModelGradient(containerGradientStr.value)
    updateColorParams()
}

const handleColorSelect = (e, type) => {
    let targetColor = targetItem.value[`colorItem${type}`]
    let result = {
        value: targetColor,
        btn: false,
        theme: "light",
        change: (color) => {
            from.value.list[from.value.targetIndex][`colorItem${type}`] = color
            from.value.list[from.value.targetIndex][`isSelected${type}`] = true
            updateModelGradient(containerGradientStr.value)
            updateColorParams()
        }
    }
    colorEvent(e, result)
    e.preventDefault();
}

let handleChangeColorPickerTimer = null
const handleChangeColorPicker = (e) => {
    // 防抖
    if (handleChangeColorPickerTimer) clearTimeout(handleChangeColorPickerTimer)
    handleChangeColorPickerTimer = setTimeout(() => {
        handleChangeColorPickerTimer = null
        // 防抖结束
        run()
    }, 300)

    const run = () => {
        console.log('run handleChangeColorPicker', e)
        let color = 1
        if (e.mode == "solid") {
            color = 1
            from.value.list[from.value.targetIndex].colorItem1 = `rgb(${e.color.r}, ${e.color.g}, ${e.color.b})`
        }
        else if (e.mode == 'linear') {
            color = e.gradients.length > 3 ? 3 : e.gradients.length
            if (e.gradients.length > 3) colorParams.value.gradients = e.gradients.slice(0, 3)
            if (e.gradients && e.gradients.length) e.gradients.map((item, index) => {
                if (index == 0) from.value.list[from.value.targetIndex].colorItem1 = `rgb(${item.color.r}, ${item.color.g}, ${item.color.b})`
                else if (index == 1) from.value.list[from.value.targetIndex].colorItem2 = `rgb(${item.color.r}, ${item.color.g}, ${item.color.b})`
                else if (index == 2) from.value.list[from.value.targetIndex].colorItem3 = `rgb(${item.color.r}, ${item.color.g}, ${item.color.b})`
            })
        }
        from.value.list[from.value.targetIndex].color = color
        updateModelGradient(containerGradientStr.value)
    }
}

// 获取容器元素
const canvasContainer = ref(null);
const PageContainerRef = ref(null)

// Three.js核心对象
let scene, camera, renderer, controls, autoRotate;
let rootModal // 保护壳模型
let frameGroup, pictureMesh;
let animationId = null;
let sceneGroup = new THREE.Group(); // 场景根元素组 所有元素都添加到这个组里
let initialCameraDistance // 初始相机距离
let zoomBase // 缩放控制器
let modalInit = false // 模型是否初始化
let isAutoRotateRunning = true // 是否正在自动旋转

// 初始化Three.js场景
function initScene() {
    // 创建场景 - 设置黑色背景
    scene = new THREE.Scene();

    scene.add(sceneGroup); // 将场景根元素组添加到场景中
    
    // 添加网格辅助线，调整颜色以适应黑色背景
    // const gridHelper = new THREE.GridHelper(20, 20, 0x333333, 0x222222);
    // scene.add(gridHelper);
    
    // 创建相机
    camera = new THREE.PerspectiveCamera(
        75, 
        canvasContainer.value.clientWidth / canvasContainer.value.clientHeight, 
        0.1, 
        1000
    )
    camera.position.set(0, 0, 15);
    camera.lookAt(0, 0, 0);
    
    // 创建渲染器
    renderer = new THREE.WebGLRenderer({ 
        antialias: true,
        alpha: true // 关键：允许背景透明
    })
    renderer.setSize(
        canvasContainer.value.clientWidth, 
        canvasContainer.value.clientHeight
    );
    renderer.shadowMap.enabled = true
    canvasContainer.value.appendChild(renderer.domElement)

    // 初始化加载器
    const loader = new GLTFLoader();
    // 加载GLB模型（路径必须正确）
    // modal4 原模型 中间无顶点
    // modal110 优化模型 中间添加少量顶点
    // modal200 大优化模型 中间均匀顶点 但是会出现闪烁现象
    spinning.value = true
    loader.load(
        '../../modal110.glb', // 模型路径（必须放在public目录下）
        (gltf) => { // 加载成功回调
            const model = gltf.scene;
            model.position.set(0, 0, 0) // 模型居中
            model.scale.set(0.1, 0.1, 0.1) // 调整模型大小
            model.rotation.x = Math.PI / 2 // 绕X轴转90度
            sceneGroup.add(model) // 将模型添加到场景根元素组
            rootModal = model
            loadEndModal(gltf)
            loadDiamondModal(gltf)
            spinning.value = false
            modalInit = true
        },
        (xhr) => { // 加载进度回调
            console.log(`加载进度：${(xhr.loaded / xhr.total) * 100}%`);
            // 英文进度
            spinningTip.value = `Please wait, loading ${parseInt((xhr.loaded / xhr.total) * 100)}%`
        },
        (error) => { // 加载失败回调
            console.error('模型加载失败:', error);
        }
    )
    
    // 添加轨道控制器
    controls = new OrbitControls(camera, renderer.domElement)
    controls.enableDamping = true // 启用阻尼效果
    controls.dampingFactor = 0.1 // 阻尼系数
    controls.enableZoom = true // 启用缩放
    controls.enablePan = false // 禁用平移
    controls.enableTouchZoom = true // 启用触摸缩放 移动端
    // controls.maxPolarAngle = Math.PI * 1 // 限制垂直旋转角度
    controls.minDistance = 8 // 限制最小距离
    controls.maxDistance = 20 // 限制最大距离

    // 初始化相机位置（记录初始距离用于重置）
    initialCameraDistance = 15 // 初始距离模型15个单位
    camera.position.z = initialCameraDistance

    // 初始化缩放控制器
    zoomBase = ZoomController(controls, camera)

    setupScreenshotWithZoom(zoomBase, controls, camera, {
        theta: Math.PI / 2 * -1,       // 水平角度30°（右侧视角）
        phi: Math.PI / 2,         // 垂直角度45°（俯视视角）
        targetX: 0,               // 模型中心X
        targetY: 0,               // 模型中心Y
        targetZ: 0,                // 模型中心Z
        unStop: true, // 不停止
    });

    // 核心：限制X轴旋转（俯仰角），只允许绕Y轴（Z轴方向）旋转
    // controls.minPolarAngle = Math.PI / 2; // 最小俯仰角 = 90度
    // controls.maxPolarAngle = Math.PI / 2; // 最大俯仰角 = 90度
    // 此时相机只能绕Y轴旋转（即沿Z轴方向旋转模型），无法上下俯仰

    // 添加光源
    addLights()
    
    // 处理窗口大小变化
    window.addEventListener('resize', handleResize)
    
    // 开始渲染循环
    animate()
}

// 所有钻石模型
let allDiamonds = [];
// 镶钻 - 导入钻石模型 批量添加到对应的位置上
const loadDiamondModal = (gltf) => {
    gltf.scene.traverse((child) => {
        if (child.isMesh) {
            const mesh = child;
            // 计算手机壳关键参数
            const phoneCaseBox = new THREE.Box3().setFromObject(mesh);
            const phoneCaseCenter = phoneCaseBox.getCenter(new THREE.Vector3());
            // 手机壳尺寸（宽、高、厚）
            const phoneCaseWidth = phoneCaseBox.max.x - phoneCaseBox.min.x;
            const phoneCaseHeight = phoneCaseBox.max.y - phoneCaseBox.min.y;
            const phoneCaseThickness = phoneCaseBox.max.z - phoneCaseBox.min.z; // 壳厚度
            // 引入钻石模型 初始化加载器
            const loader = new GLTFLoader();
            loader.load( // 加载GLB模型（路径必须正确）
                '../../zuan22.glb', // 模型路径（必须放在public目录下）
                (zuanGltf) => { // 加载成功回调
                    // 钻石大小 保护壳宽度的比例 0.13
                    let size = 0.05
                    const zuanScale = phoneCaseWidth * size * 0.01;
                    const zuan = zuanGltf.scene;
                    // 绕X轴转90度
                    zuan.rotation.x = Math.PI / 2
                    zuan.rotation.z = Math.PI
                    zuan.scale.set(zuanScale, zuanScale, zuanScale) // 调整模型大小
                    // 创建“中心亮、边缘暗”的渐变发光纹理（模拟内部发光的真实衰减）
                    function createGlowTexture() {
                        const canvas = document.createElement('canvas');
                        canvas.width = 256;
                        canvas.height = 256;
                        const ctx = canvas.getContext('2d');

                        // 绘制径向渐变：中心白色（亮）→ 边缘透明（暗）
                        const gradient = ctx.createRadialGradient(
                            canvas.width/2, canvas.height/2, 0,    // 渐变中心
                            canvas.width/2, canvas.height/2, 128   // 渐变边缘
                        );
                        gradient.addColorStop(0, 'rgba(230, 247, 255, 1)');  // 中心：自发光颜色（不透明）
                        gradient.addColorStop(1, 'rgba(230, 247, 255, 0)');  // 边缘：透明（不发光）

                        ctx.fillStyle = gradient;
                        ctx.fillRect(0, 0, canvas.width, canvas.height);

                        // 转为 Three.js 纹理
                        const texture = new THREE.CanvasTexture(canvas);
                        texture.needsUpdate = true;
                        return texture;
                    }
                    // 创建钻石材质
                    const diamondMaterial = new THREE.MeshPhysicalMaterial({
                        transparent: true,        // 半透明
                        opacity: 0.9,             // 透明度
                        color: '#ffffff',        // 钻石本身无色，设为白色
                        roughness: 0,           // 0 = 极度光滑（关键）
                        metalness: 0,           // 非金属
                        transmission: 1,        // 1 = 完全透明（关键，允许光线穿过）
                        ior: 2.5,              // 钻石折射率（关键参数，默认1.5）
                        side: THREE.DoubleSide, // 钻石可能有内部结构，双面可见
                        clearcoat: 1,           // 增强表面反射（类似镀膜效果）
                        clearcoatRoughness: 0,   // 表面反射无粗糙
                        reflectivity: 1,         // 反射强度（1 为最大）

                        // 核心：自发光设置（让钻石从内部发光）
                        emissive: 0xe6f7ff,      // 自发光颜色（冷色调：淡青蓝，像钻石的“荧光感”）
                        emissiveIntensity: 0.8,  // 自发光亮度（0.5-1之间，太亮像灯泡）
                        emissiveMap: createGlowTexture(), // 新增“渐变发光纹理”（模拟中心到边缘的发光衰减
                    });
                    zuan.traverse((child) => {
                        if (child.isMesh) {
                            child.material = diamondMaterial
                            // 批量添加到手机壳上
                            let allZ = -0.28
                            let diamondList = [
                                { x: -4.3, y: -65, z: allZ },
                                { x: -3.9, y: -65, z: allZ },
                                { x: -3.5, y: -65, z: allZ },
                                { x: -3.1, y: -65, z: allZ },
                                { x: -2.7, y: -65, z: allZ },
                                { x: -2.3, y: -65, z: allZ },
                                { x: -1.9, y: -65, z: allZ },
                                { x: -1.5, y: -65, z: allZ },
                                { x: -1.1, y: -65, z: allZ },
                                { x: -0.7, y: -65, z: allZ },
                                { x: -0.3, y: -65, z: allZ },
                                { x: 0.1, y: -65, z: allZ },
                                { x: 0.5, y: -65, z: allZ },
                                { x: 0.9, y: -65, z: allZ },
                                { x: 1.3, y: -65, z: allZ },
                                { x: 1.7, y: -65, z: allZ },
                                { x: 2.1, y: -65, z: allZ },
                                { x: 2.5, y: -65, z: allZ },
                                { x: 2.9, y: -65, z: allZ },
                                { x: 3.3, y: -65, z: allZ },
                                { x: 3.7, y: -65, z: allZ },
                                { x: 4.1, y: -65, z: allZ },
                                { x: 4.5, y: -65, z: allZ },

                                { x: 4.7, y: -61, z: allZ },
                                { x: 4.7, y: -57, z: allZ },
                                { x: 4.7, y: -53, z: allZ },
                                { x: 4.7, y: -49, z: allZ },
                                { x: 4.7, y: -45, z: allZ },
                                { x: 4.7, y: -41, z: allZ },
                                { x: 4.7, y: -37, z: allZ },
                                { x: 4.7, y: -33, z: allZ },
                                { x: 4.7, y: -29, z: allZ },
                                { x: 4.7, y: -25, z: allZ },
                                { x: 4.7, y: -21, z: allZ },
                                { x: 4.7, y: -17, z: allZ },
                                { x: 4.7, y: -13, z: allZ },
                                { x: 4.7, y: -9, z: allZ },
                                { x: 4.7, y: -5, z: allZ },
                                { x: 4.7, y: -1, z: allZ },
                                { x: 4.7, y: 3, z: allZ },
                                { x: 4.7, y: 7, z: allZ },
                                { x: 4.7, y: 11, z: allZ },
                                { x: 4.7, y: 15, z: allZ },
                                { x: 4.7, y: 19, z: allZ },
                                { x: 4.7, y: 23, z: allZ },
                                { x: 4.7, y: 27, z: allZ },
                                { x: 4.7, y: 31, z: allZ },
                                { x: 4.7, y: 35, z: allZ },
                                { x: 4.7, y: 39, z: allZ },
                                { x: 4.7, y: 43, z: allZ },
                                { x: 4.7, y: 47, z: allZ },
                                { x: 4.7, y: 51, z: allZ },
                                { x: 4.7, y: 55, z: allZ },
                                { x: 4.7, y: 59, z: allZ },
                                { x: 4.7, y: 63, z: allZ },

                                { x: 4.3, y: 65, z: allZ },
                                { x: 3.9, y: 65, z: allZ },
                                { x: 3.5, y: 65, z: allZ },
                                { x: 3.1, y: 65, z: allZ },
                                { x: 2.7, y: 65, z: allZ },
                                { x: 2.3, y: 65, z: allZ },
                                { x: 1.9, y: 65, z: allZ },
                                { x: 1.5, y: 65, z: allZ },
                                { x: 1.1, y: 65, z: allZ },
                                { x: 0.7, y: 65, z: allZ },
                                { x: 0.3, y: 65, z: allZ },
                                { x: -0.1, y: 65, z: allZ },
                                { x: -0.5, y: 65, z: allZ },
                                { x: -0.9, y: 65, z: allZ },
                                { x: -1.3, y: 65, z: allZ },
                                { x: -1.7, y: 65, z: allZ },
                                { x: -2.1, y: 65, z: allZ },
                                { x: -2.5, y: 65, z: allZ },
                                { x: -2.9, y: 65, z: allZ },
                                { x: -3.3, y: 65, z: allZ },
                                { x: -3.7, y: 65, z: allZ },
                                { x: -4.1, y: 65, z: allZ },
                                { x: -4.5, y: 65, z: allZ },

                                { x: -4.7, y: 62, z: allZ },
                                { x: -4.7, y: 58, z: allZ },
                                { x: -4.7, y: 54, z: allZ },
                                { x: -4.7, y: 50, z: allZ },
                                { x: -4.7, y: 46, z: allZ },
                                { x: -4.7, y: 42, z: allZ },
                                { x: -4.7, y: 38, z: allZ },
                                { x: -4.7, y: 34, z: allZ },
                                { x: -4.7, y: 30, z: allZ },
                                { x: -4.7, y: 26, z: allZ },
                                { x: -4.7, y: 22, z: allZ },
                                { x: -4.7, y: 18, z: allZ },
                                { x: -4.7, y: 14, z: allZ },
                                { x: -4.7, y: 10, z: allZ },
                                { x: -4.7, y: 6, z: allZ },
                                { x: -4.7, y: 2, z: allZ },
                                { x: -4.7, y: -2, z: allZ },
                                { x: -4.7, y: -6, z: allZ },
                                { x: -4.7, y: -10, z: allZ },
                                { x: -4.7, y: -14, z: allZ },
                                { x: -4.7, y: -18, z: allZ },
                                { x: -4.7, y: -22, z: allZ },
                                { x: -4.7, y: -26, z: allZ },
                                { x: -4.7, y: -30, z: allZ },
                                { x: -4.7, y: -34, z: allZ },
                                { x: -4.7, y: -38, z: allZ },
                                { x: -4.7, y: -42, z: allZ },
                                { x: -4.7, y: -46, z: allZ },
                                { x: -4.7, y: -50, z: allZ },
                                { x: -4.7, y: -54, z: allZ },
                                { x: -4.7, y: -58, z: allZ },
                                { x: -4.7, y: -62, z: allZ },
                            ]
                            diamondList.map(item => {
                                // 调整位置 加入场景
                                const diamond = zuan.clone();
                                diamond.position.set(
                                    item.x * phoneCaseWidth * 0.01,
                                    item.y * phoneCaseHeight * 0.01,
                                    item.z * phoneCaseThickness * 0.01
                                );
                                sceneGroup.add(diamond) // 将模型添加到场景组
                                allDiamonds.push(diamond); // 暂存所有钻石
                            })
                        }
                    })
                },
                (xhr) => { // 加载进度回调
                    console.log(`加载进度：${(xhr.loaded / xhr.total) * 100}%`);
                },
                (error) => { // 加载失败回调
                    console.error('钻石模型加载失败:', error);
                }
            )
        }
    });
}
const toggleDiamonds = () => { // 切换显示钻石
    const currentState = allDiamonds[0]?.visible ?? true;
    if (currentState) {
        allDiamonds.forEach(diamond => {
            if (diamond && diamond.isObject3D) {
                diamond.visible = false;
            }
        })
    } else {
        allDiamonds.forEach(diamond => {
            if (diamond && diamond.isObject3D) {
                diamond.visible = true;
            }
        });
    }
}

// 解析渐变字符串获取颜色点（支持 RGB 和十六进制颜色）
const parseGradient = (gradientStr) => {
    // 提取渐变方向（如 to bottom、75deg 等）
    const directionMatch = gradientStr.match(/linear-gradient\(([^,]+?),/);
    const direction = directionMatch ? directionMatch[1].trim() : 'to bottom';
    
    // 增强正则：支持两种颜色格式
    // 1. 十六进制：#ff0000 或 #f00
    // 2. RGB：rgb(255, 0, 0) 或 rgb(255,  0,  0)（允许空格）
    const colorParts = gradientStr.match(
        /(#[0-9a-fA-F]{3,6}|rgb\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*\))\s+\d+%/g
    );

    if (!colorParts || colorParts.length < 2) {
        console.warn('无效的渐变字符串，使用默认渐变');
        return {
            direction,
            colorStops: [
                { color: new THREE.Color(1, 0, 0), position: 0 }, // 红色
                { color: new THREE.Color(0, 1, 0), position: 1 }  // 绿色
            ]
        };
    }
    
    // 解析颜色点（同时处理 RGB 和十六进制）
    const colorStops = colorParts.map(part => {
        // 拆分颜色字符串和位置（按最后一个空格分割）
        const lastSpaceIndex = part.lastIndexOf(' ');
        const colorStr = part.substring(0, lastSpaceIndex).trim();
        const positionStr = part.substring(lastSpaceIndex).trim();
        const position = parseFloat(positionStr.replace('%', '')) / 100;

        let threeColor;
        // 区分 RGB 和十六进制格式
        if (colorStr.startsWith('rgb(')) {
            // 解析 RGB 颜色（如 rgb(255, 0, 0)）
            const rgbMatch = colorStr.match(/rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/);
            if (rgbMatch) {
                // RGB 值从 0-255 转换为 0-1（Three.js 要求）
                const r = Math.max(0, Math.min(255, parseInt(rgbMatch[1]))) / 255;
                const g = Math.max(0, Math.min(255, parseInt(rgbMatch[2]))) / 255;
                const b = Math.max(0, Math.min(255, parseInt(rgbMatch[3]))) / 255;
                threeColor = new THREE.Color(r, g, b);
            } else {
                console.warn('无效的 RGB 格式:', colorStr);
                threeColor = new THREE.Color(0, 0, 0); // 默认黑色
            }
        } else {
            // 处理十六进制颜色（如 #ff0000 或 #f00）
            threeColor = new THREE.Color(colorStr);
        }
        
        return {
            color: threeColor,
            position: Math.max(0, Math.min(1, position)) // 确保位置在 0-1 之间
        };
    }).sort((a, b) => a.position - b.position); // 按位置排序
    
    // 调试输出（方便验证）
    console.log('解析结果：', colorStops.map(s => ({
        color: `rgb(${Math.round(s.color.r*255)}, ${Math.round(s.color.g*255)}, ${Math.round(s.color.b*255)})`,
        position: s.position
    })));
    
    return { direction, colorStops };
};


// 在两个颜色之间插值
const interpolateColor = (colorStops, ratio) => {

    // 1. 处理比例在第一个停止点之前的情况（显示第一个颜色）
        if (ratio <= colorStops[0].position) {
        return colorStops[0].color.clone();
    }

    // 2. 处理比例在最后一个停止点之后的情况（显示最后一个颜色）
    if (ratio >= colorStops[colorStops.length - 1].position) {
        return colorStops[colorStops.length - 1].color.clone();
    }

    // 3. 找到当前比例所在的两个停止点之间
    for (let i = 0; i < colorStops.length - 1; i++) {
        const start = colorStops[i];
        const end = colorStops[i + 1];
        
        // 比例在当前区间内
        if (ratio >= start.position && ratio <= end.position) {
            // 计算在当前区间内的相对比例（0~1）
            const segmentRatio = (ratio - start.position) / (end.position - start.position);
            // 混合两个颜色
            return start.color.clone().lerp(end.color, segmentRatio);
        }
    }

    // 兜底返回第一个颜色
    return colorStops[0].color.clone();
};

// 保护壳材质 用于记录当前材质属性
let caseMaterials = [];
// 0.添加保护壳模型，接受渐变字符串参数
const loadEndModal = async (gltf, init = true, gradientStr = 'linear-gradient(to bottom, rgb(151, 97, 233) 0%, rgb(64, 208, 233) 100%)') => {

    // gradientStr = 'linear-gradient(to bottom, #ef4444 0%, #ef4444 100%)'

    // 解析渐变配置
    const { colorStops } = parseGradient(gradientStr);

    console.log('colorStops',colorStops)

    gltf.scene.traverse((child) => {
        if (child.isMesh) {
            const mesh = child;
            const posAttr = mesh.geometry.attributes.position;
            const vertexCount = posAttr.count;

            // （Z轴渐变颜色计算）
            const zCoords = [];
            for (let i = 0; i < vertexCount; i++) {
                zCoords.push(posAttr.getZ(i));
            }
            zCoords.sort((a, b) => a - b);
            const minZ = zCoords[0];
            const maxZ = zCoords[vertexCount - 1];
            const zRange = maxZ - minZ;

            const colorsArr = [];
            for (let i = 0; i < vertexCount; i++) {
                // 计算Z轴比例
                const zRatio = zRange > 0 ? (posAttr.getZ(i) - minZ) / zRange : 0;
                const safeRatio = Math.max(0, Math.min(1, zRatio));
                
                // 根据解析的渐变计算当前颜色
                const currentColor = interpolateColor(colorStops, safeRatio);
                colorsArr.push(currentColor.r, currentColor.g, currentColor.b);
            }
            const colorBuffer = new Float32Array(colorsArr);
            mesh.geometry.setAttribute('color', new THREE.BufferAttribute(colorBuffer, 3));
            // 创建材质时，先清空数组（避免重复添加，针对重新加载的场景）
            if (caseMaterials.length === 0) {
                // 核心：创建材质并添加到全局数组
                const material = new THREE.MeshPhysicalMaterial({
                    vertexColors: true,
                    transparent: true,
                    opacity: 0.8,           // 初始透明度
                    metalness: 0.05,
                    roughness: 0.05,
                    reflectivity: 0.9,
                    transmission: 1,      // 初始透射率
                    ior: 1.5,
                    clearcoat: 1,
                    clearcoatRoughness: 0.01,
                    side: THREE.DoubleSide,
                });
                mesh.material = material;
                caseMaterials.push(material); // 暂存
            } else {
                // 如果已有材质，直接复用（避免重复创建）
                mesh.material = caseMaterials[0];
            }
            if (init) {
                addImageToCase(mesh); // 添加图片到保护壳内部
                addGlitterToCase(mesh); // 添加粉粒
            }
        }
    });
};
// 更改保护壳材质属性
function updateCaseMaterialProps(props) {
    // 遍历所有保护壳材质，批量修改属性
    caseMaterials.forEach(material => {
        if (material && material.isMaterial) { // 确保是有效的材质实例
            Object.entries(props).forEach(([key, value]) => {
                // 只允许修改材质支持的属性（避免误操作）
                if (material.hasOwnProperty(key)) {
                    material[key] = value;
                } else {
                    console.warn(`材质不支持 ${key} 属性`);
                }
            });
            material.needsUpdate = true; // 通知Three.js材质已更新
        }
    });
}
const handleChangeSheen = () => {
    let light = targetItem.value.light // 1亮光 2哑光
    let glossy = { // 亮光模式
        roughness: 0.05,           // 表面极度光滑
        clearcoat: 1,              // 最强清漆层（模拟抛光效果）
        clearcoatRoughness: 0.01,  // 清漆层无粗糙
        reflectivity: 0.9,          // 高反射率（增强镜面效果）
        transparent: true,
        opacity: 0.8,           // 初始透明度
        transmission: 1,      // 初始透射率
    }
    let matte = { // 哑光模式
        roughness: 1,            // 表面明显粗糙
        clearcoat: 0,              // 无清漆层（取消抛光）
        clearcoatRoughness: 1,     // 清漆层完全粗糙（不影响，因为clearcoat为0）
        reflectivity: 0,          // 低反射率（减少镜面效果）
        transparent: false,
        opacity: 1,           // 初始透明度
        transmission: 0,      // 无透射率（取消透明效果）
    }
    updateCaseMaterialProps(light == 1 ? glossy : matte)
}

// 1. 加载图片纹理
let logoTexture1; // 卡片 - 前
let logoTexture2; // 卡片 - 后
function loadLogoTexture1() {
    return new Promise((resolve) => {
        const loader = new THREE.TextureLoader();
        loader.load('../../card2.png', (texture) => {
            texture.minFilter = THREE.LinearFilter;
            texture.magFilter = THREE.LinearFilter;
            // 确保图片的颜色正确 
            if (texture.colorSpace !== undefined) texture.colorSpace = THREE.SRGBColorSpace; 
            else texture.encoding = THREE.sRGBEncoding; // 适配 Three.js 旧版本（r151 及以下）
            texture.needsUpdate = true;
            logoTexture1 = texture;
            resolve(texture);
        });
    });
}
function loadLogoTexture2() {
    return new Promise((resolve) => {
        const loader = new THREE.TextureLoader();
        loader.load('../../card1.png', (texture) => {
            texture.minFilter = THREE.LinearFilter;
            texture.magFilter = THREE.LinearFilter;
            // 确保图片的颜色正确 
            if (texture.colorSpace !== undefined) texture.colorSpace = THREE.SRGBColorSpace; 
            else texture.encoding = THREE.sRGBEncoding; // 适配 Three.js 旧版本（r151 及以下）
            texture.needsUpdate = true;
            logoTexture2 = texture;
            resolve(texture);
        });
    });
}

// 2.添加图片到保护壳内部和外部
const addImageToCase = async (mesh) => {
    await loadLogoTexture1(); // 加载第一张图片纹理
    await loadLogoTexture2(); // 加载第二张图片纹理
    
    // --------------------------
    // 计算手机壳关键参数
    const phoneCaseBox = new THREE.Box3().setFromObject(mesh);
    const phoneCaseCenter = phoneCaseBox.getCenter(new THREE.Vector3());
    // 手机壳尺寸（宽、高、厚）
    const phoneCaseWidth = phoneCaseBox.max.x - phoneCaseBox.min.x;
    const phoneCaseHeight = phoneCaseBox.max.y - phoneCaseBox.min.y;
    const phoneCaseThickness = phoneCaseBox.max.z - phoneCaseBox.min.z; // 壳厚度
    // 手机壳Z轴边界（内侧和外侧）
    const innerZ = phoneCaseBox.min.z; // 壳最内侧Z值
    const outerZ = phoneCaseBox.max.z; // 壳最外侧Z值
    // --------------------------
    // 图片尺寸：0.8倍手机壳（保持比例）
    const imageWidth = phoneCaseWidth * 0.9;
    const imageHeight = phoneCaseHeight * 0.93;
    // --------------------------
    // 核心：计算两个图片的Z轴位置（前后分开）
    const frontImageZ = innerZ + 0.6;  // 前面图片（内侧）位置
    const backImageZ = outerZ - 1;   // 后面图片（外侧）位置
    // --------------------------
    // 创建前面图片模型（内侧，logoTexture1）
    const frontGeometry = new THREE.PlaneGeometry(imageWidth, imageHeight);
    const frontMaterial = new THREE.MeshBasicMaterial({
        map: logoTexture1,
        // side: THREE.DoubleSide,
    });
    const frontImageModel = new THREE.Mesh(frontGeometry, frontMaterial);
    frontImageModel.position.copy(phoneCaseCenter);
    frontImageModel.position.z = frontImageZ; // 设置前面图片Z轴位置
    // frontImageModel.rotation.y = Math.PI; // 绕Y轴翻转180度
    
    // 创建后面图片模型（外侧，logoTexture2）
    const backGeometry = new THREE.PlaneGeometry(imageWidth, imageHeight);
    const backMaterial = new THREE.MeshBasicMaterial({
        map: logoTexture2,
        // side: THREE.DoubleSide,
    });
    const backImageModel = new THREE.Mesh(backGeometry, backMaterial);
    backImageModel.position.copy(phoneCaseCenter);
    backImageModel.position.z = backImageZ; // 设置后面图片Z轴位置
    // 后面图片可能需要不同的旋转角度，根据实际显示情况调整
    backImageModel.rotation.y = Math.PI; // 绕Y轴翻转180度

    // --------------------------
    // 调试信息：打印两个图片的位置和尺寸
    console.log('前面图片位置（Z轴）：', frontImageModel.position.z);
    console.log('后面图片位置（Z轴）：', backImageModel.position.z);
    console.log('手机壳Z轴范围：', innerZ, '~', outerZ);
    console.log('图片尺寸：', imageWidth, 'x', imageHeight);

    // 添加到场景
    sceneGroup.add(frontImageModel) // 将模型添加到场景组
    sceneGroup.add(backImageModel) // 将模型添加到场景组
    
    // 可以保存引用以便后续操作
    mesh.userData.frontImage = frontImageModel;
    mesh.userData.backImage = backImageModel;

    // 设置自动旋转控制器
    autoRotate = setupAutoRotate(
        sceneGroup,          // 你的模型对象
        controls,       // 轨道控制器实例
        0.01,          // 旋转速度
        1000            // 1秒无操作后恢复自动旋转
    );
}


// 3.修改渐变颜色
// 接收场景对象和新的渐变字符串
const updateModelGradient = (newGradientStr) => {

    // test
    // newGradientStr = 'linear-gradient(to bottom, rgb(239, 15, 15) 0%, rgb(239, 0, 255) 10%, rgb(0, 255, 68) 100%)'

    if (!modalInit) return

    console.log('更新渐变：', newGradientStr);

    const { colorStops } = parseGradient(newGradientStr);
    console.log('解析的颜色停止点：', colorStops.map(s => `${s.color.getHexString()} ${s.position*100}%`));

    scene.traverse((child) => {
        if (child.isMesh) {
            const mesh = child;
            const posAttr = mesh.geometry.attributes.position;
            if (!posAttr) return;

            const vertexCount = posAttr.count;
            const zCoords = [];
            for (let i = 0; i < vertexCount; i++) {
                zCoords.push(posAttr.getZ(i));
            }
            zCoords.sort((a, b) => a - b);
            const minZ = zCoords[0];
            const maxZ = zCoords[vertexCount - 1];
            let zRange = maxZ - minZ;

            // console.log('zCoords',zCoords)

            if (zRange <= 0) return;

            // Z轴归零处理
            const zeroBasedZ = (z, index) => {
                return z - minZ
            }
            // console.log('原始Z范围：', minZ, '~', maxZ, '，归零后：0 ~', zRange);


            const colorsArr = [];
            for (let i = 0; i < vertexCount; i++) {
                const z = posAttr.getZ(i);
                const zZero = zeroBasedZ(z);
                // 计算Z值在总范围中的比例（0~1），对应渐变的整体进度
                const zRatio = zZero / zRange;
                const safeRatio = Math.max(0, Math.min(1, zRatio));
                // 关键修复：直接使用Z轴比例计算颜色，不硬编码调整（遵循CSS规则）
                const currentColor = interpolateColor(colorStops, safeRatio);
                colorsArr.push(currentColor.r, currentColor.g, currentColor.b);
            }

            // 更新颜色
            const colorBuffer = new Float32Array(colorsArr);
            if (mesh.geometry.attributes.color) {
                mesh.geometry.attributes.color.array = colorBuffer;
                mesh.geometry.attributes.color.needsUpdate = true;
            } else {
                mesh.geometry.setAttribute('color', new THREE.BufferAttribute(colorBuffer, 3));
            }

            mesh.material.needsUpdate = true;
        }
    });
};

// 全局变量：存储跟随相机的光源（方便后续访问）
let cameraPointLight;
// 添加光源（观察者=光源）
function addLights() {
    let color = '#ffffff';
    // 1. 基础环境光（弱强度，仅用于补光，避免暗部过黑）
    const ambientLight = new THREE.AmbientLight(color, 1); // 强度降低到0.2，避免干扰主光源
    scene.add(ambientLight);
    // 2. 核心：跟随相机的点光源（观察者即光源，位置与相机完全一致）
    // cameraPointLight = new THREE.PointLight(color, 1.2, 50, 2);
    // // 参数说明：
    // // 光源颜色（白色）
    // // 1.2：光源强度（可调整，避免过亮）
    // // 50：光照最大距离（覆盖模型即可，过大会影响性能）
    // // 2：光照衰减指数（模拟真实世界光照，距离越远越暗）
    // // 设置位置
    // cameraPointLight.position.set(0, 0, 0);
    // // 可选：开启点光源阴影（增强真实感，适合手机壳半透明效果）
    // cameraPointLight.castShadow = true;
    // // 配置阴影分辨率（越高越清晰，性能消耗也越大）
    // cameraPointLight.shadow.mapSize.width = 1024;
    // cameraPointLight.shadow.mapSize.height = 1024;
    // // 配置阴影相机范围（确保覆盖模型）
    // cameraPointLight.shadow.camera.near = 0.1;
    // cameraPointLight.shadow.camera.far = 30;
    // // 关键：将点光源添加到相机上，实现“相机移动=光源移动”
    // // 无需手动设置position，子对象会自动继承父对象（相机）的位置
    // camera.add(cameraPointLight); 

    // 3. 保留辅助方向光（弱强度，补充环境细节，避免单一光源单调）
    const directionalLight2 = new THREE.DirectionalLight(color, 0.2); // 强度降低到0.2
    directionalLight2.position.set(15, 5, 5);
    scene.add(directionalLight2);

    // 4. 添加强光源（钻石需要强光才能体现折射/反射）
    const directionalLight = new THREE.DirectionalLight('#ffffff', 3);
    directionalLight.position.set(25, 20, -15);
    scene.add(directionalLight);

    const directionalLight3 = new THREE.DirectionalLight('#ffffff', 3);
    directionalLight3.position.set(0, 20, -15);
    scene.add(directionalLight3);
}

// 处理窗口大小变化
function handleResize() {
    if (!canvasContainer.value) return;
    
    const width = canvasContainer.value.clientWidth;
    const height = canvasContainer.value.clientHeight;
    
    camera.aspect = width / height;
    camera.updateProjectionMatrix();
    
    renderer.setSize(width, height);
}

// 动画循环
function animate() {
    animationId = requestAnimationFrame(animate)
    controls.update()
    // 更新自动旋转
    if (autoRotate && isAutoRotateRunning) autoRotate.update();
    renderer.render(scene, camera)
}

// 2. 隐藏 Canvas 时，停止动画循环
function hideCanvasAndCleanup() {
    cancelAnimationFrame(animationId);
    // 停止自动旋转并清理事件（避免内存泄漏）
    if (autoRotate) {
        autoRotate.stop();
        autoRotate.dispose(); // 调用之前控制器的dispose方法，清理事件监听
    }
}

// 切换样式后 同步模型状态 传入变更前的样式 与 变更后的样式
const handleChangeStyleUpdateModal = (lastItem, item) => {
    console.log('handleChangeStyleUpdateModal lastItem item', lastItem, item)
    // 哑光亮光切换
    if (lastItem.light != item.light) handleChangeSheen()
    // 是否镶钻切换
    if (lastItem.zuan != item.zuan) toggleDiamonds()
    // 粉粒大小切换
    if (lastItem.fen != item.fen) toggleGlitterSize()
    updateModelGradient(containerGradientStr.value) // 切换渐变
    updateColorParams() // 同步颜色到渐变选择器
}

const handleAddStyleItem = () => {
    let lastItem = targetItem.value
    lastItem = JSON.parse(JSON.stringify(lastItem))
    let item = {
        light: 1, // 1: 亮光 2: 哑光
        color: 1, // 1: 单色 2: 双色 3: 三色
        zuan: 1, // 1: 带钻 0: 无钻
        fen: 1, // 1: 大 2: 小
        colorItem1: 'rgb(239, 15, 15)',
        colorItem2: 'rgb(239, 15, 15)',
        colorItem3: 'rgb(239, 15, 15)',
        isSelected1: false,
        isSelected2: false,
        isSelected3: false,
        number: 1,
    }
    from.value.list.push(item)
    from.value.targetIndex = from.value.list.length - 1
    handleChangeStyleUpdateModal(lastItem, item)
    scrollTo(0, PageContainerRef.value) // 滚动到页面顶部
}

const handleRemoveStyleItem = () => {
    let targetIndex = from.value.targetIndex
    if (from.value.list.length == 1) return
    let lastItem = targetItem.value
    lastItem = JSON.parse(JSON.stringify(lastItem))
    from.value.list.splice(targetIndex, 1)
    from.value.targetIndex = from.value.list.length - 1
    let item = targetItem.value
    handleChangeStyleUpdateModal(lastItem, item)
}

const handleClickStyle = (index) => {
    if (from.value.targetIndex == index) return
    let lastItem = targetItem.value
    lastItem = JSON.parse(JSON.stringify(lastItem))
    from.value.targetIndex = index
    let item = from.value.list[index]
    handleChangeStyleUpdateModal(lastItem, item)
}

// captureModelScreenshot
// 将当前所有的样式的截图汇总
const getAllStyleModalScreenshot = async () => {
    spinning.value = true
    spinningTip.value = 'Loading...'
    let index = 0
    let targetIndex = from.value.list.length - 1
    let list = []
    for (let index = 0; index < from.value.list.length; index++) {
        let orderItem = {
            img: '',
            gradientStr: '', // containerGradientStr.value
            style: from.value.list[index],
        }
        list.push(orderItem)
    }
    // 停止自动旋转
    if (autoRotate) {
        isAutoRotateRunning = false
        autoRotate.pauseForScreenshot();
        sceneGroup.rotation.y = 0 // 重置模型旋转角度
    }
    if (zoomBase) {
        // zoomBase.resetZoom(initialCameraDistance) // 重置到初始缩放大小
        zoomBase.resetZoom(12) // 重置到初始缩放大小(稍大)
        await setupScreenshotWithZoom(zoomBase, controls, camera, {
            theta: Math.PI / 2 * -1,       // 水平角度30°（右侧视角）
            phi: Math.PI / 2,         // 垂直角度45°（俯视视角）
            targetX: 0,               // 模型中心X
            targetY: 0,               // 模型中心Y
            targetZ: 0                // 模型中心Z
        });
    }
    // 逐个切换模型 等待片刻后截图 全部截图完毕后返回
    const loop = () => {
        if (index > targetIndex) {
            // 全部截图完毕后返回
            spinning.value = false
            orderList.value = list
            console.log('getAllStyleModalScreenshot done orderList', orderList.value)
            isCustom.value = false // 显示订单界面
            hideCanvasAndCleanup() // 隐藏canvas
            localStorage.setItem('orderList', JSON.stringify(orderList.value))
            orderFrom.value.email = '' // 重置表单
            orderFrom.value.phone = ''
            orderFrom.value.name = ''
            orderFrom.value.remark = ''
            scrollTo(0, PageContainerRef.value) // 滚动到页面顶部
            return
        }
        handleClickStyle(index)
        setTimeout(() => {
            captureModelScreenshot(scene, camera, {
                width: 500,
                height: 500,
                transparent: true,
                format: 'image/png',
            })
            .then((res) => {
                list[index].img = res
                list[index].gradientStr = list[index].style.color == 1 ? list[index].style.colorItem1 : containerGradientStr.value
                spinningTip.value = `Loading... (${index + 1} / ${targetIndex + 1})`
                index++
                loop()
            })
            .catch(err => {
                console.error(`第${index}个模型截图失败：`, err)
                index++
                loop()
            })
        }, 1000)
    }
    loop()
}

const submit = () => {
    getAllStyleModalScreenshot()
}

const deleteOrderItem = (index) => {
    orderList.value.splice(index, 1)
    localStorage.setItem('orderList', JSON.stringify(orderList.value))
}

const submitOrder = () => {
    // 校验 英文提示
    let err = ''
    let emailReg = /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/
    if (!orderFrom.value.email) err = 'Please enter your email address'
    else if (!emailReg.test(orderFrom.value.email)) err = 'Please enter a valid email address'
    else if (!orderFrom.value.phone) err = 'Please enter your phone number'
    else if (!orderFrom.value.name) err = 'Please enter your name'
    if (err) return message.error(err)
    // 提交订单
    let order = {
        email: orderFrom.value.email,
        phone: orderFrom.value.phone,
        name: orderFrom.value.name,
    }
    let orderListPromiseList = []
    orderList.value.forEach(item => {
        let promiseItem = new Promise((resolve, reject) => {
            let params = {
                grain: item.style.fen == 1 ? '小' : '大',
                gloss: item.style.light == 1 ? '亮光' : '哑光',
                color_type: item.style.color == 1 ? '单色' : item.style.color == 2 ? '双色' : '三色',
                diamond: item.style.zuan == 1 ? '是' : '否',
                name: order.name,
                mobile: order.phone,
                email: order.email,
                color_info: item.gradientStr,
                base_img: item.img,
            }
            if (orderFrom.value.remark) params.remark = orderFrom.value.remark
            fetch('save', params)
            .then(res => {
                resolve(res)
            })
            .catch(err => {
                reject(err)
            })

            // setTimeout(() => {
            //     resolve({})
            // }, 500);
        })
        orderListPromiseList.push(promiseItem)
    })
    // 开始请求
    spinning.value = true
    spinningTip.value = 'Loading...'
    Promise.all(orderListPromiseList)
    .then(res => {
        console.log('提交订单成功', res)
        openSuccessModal.value = true
    })
    .catch(err => {
        console.error('提交订单失败', err)
        message.error(err || 'Submit order failed')
    })
    .finally(() => {
        spinning.value = false
    })
}

const showSetting = ref(false)
const setting = ref({
    modalType: 1, // 1: 兼容模型 2: 实验性模型
    bg: 1, // 1: 白色背景 2: 黑色背景
})
const handleSetting = () => {
    showSetting.value = !showSetting.value
}
const handleChangeSetting = (key, value) => {
    if (setting.value[key] == value) return
    setting.value[key] = value
}

// 组件挂载时初始化
onMounted(() => {
    if (canvasContainer.value) {
        initScene();
    }
    window.addEventListener('resize', matchMobile)

    let initLang = 'en'
    // 参数
    const urlParams = new URLSearchParams(window.location.search)
    const lang = urlParams.get('lang')
    if (lang) {
        if (lang == 'JP') initLang = 'jp'
        if (lang == 'US') initLang = 'en'
    }
    else {
        const referer = document.referrer
        if (referer == 'https://koduckya.shop/') initLang = 'jp'
        if (referer == 'https://ficrcu-qn.myshopify.com/') initLang = 'en'
    }
    changeLanguage(initLang)
});

// 组件卸载时清理
onUnmounted(() => {
    window.removeEventListener('resize', handleResize);
    
    if (animationId) {
        cancelAnimationFrame(animationId);
    }
    
    if (canvasContainer.value && renderer?.domElement) {
    canvasContainer.value.removeChild(renderer.domElement);
    }
    
    if (scene) {
    scene.dispose();
    }
    if (renderer) {
    renderer.dispose();
    }

    window.removeEventListener('resize', matchMobile)
});
</script>

<style lang="scss">
//匹配title="radial"的标签
[title="radial"] { // 隐藏径向渐变功能
    display: none;
}

// 隐藏渐变角度选择
.grad-wrapper {
    .flex-row {
        // display: none;
        opacity: 0;
    }

    .grad-bar {
        // flex: 1;
    }
}

.grad-bar {
    // pointer-events: none !important;
    .picker  {
        // 正常可触摸
        pointer-events: all !important;
    }
}

.color-list {
    display: none !important;
}
.color-alpha {
    display: none !important;
}
// color-value的子元素的div的子元素第一个div
.color-value {
    & :first-child {
        & :first-child {
            input {
                &:last-child {
                    pointer-events: none !important;
                }
            }
        }
    }
    svg {
        display: none !important;
    }
}
.color-picker-light {
    width: auto !important;
}

// 使用SCSS变量定义颜色
// 渐变背景色
$bg-color: linear-gradient(0deg, #5a88aa, #acbecc);
$hint-bg: rgba(255, 255, 255, 0.2); // 半透明白色提示框背景
$theme1Color: #1D1D1F; // 主题色1 文字与边框颜色
.PageContainer {
    width: 100%;
    height: 100vh;
    overflow: auto;
    position: relative;
    &.bg1 {
        background: #fff;
    }
    &.bg2 {
        background: #121212;
        .titleBar {
            color: #fff !important;
        }
        .orderItem {
            .right {
                color: #fff !important;
            }
        }
        .orderFrom {
            color: #fff !important;
        }
        .styleList {
            .styleListItem {
                color: #fff !important;
            }
        }
        .canvasRight {
            .item {
                color: #fff !important;
            }
        }
        input {
            color: #fff !important;
        }
    }


    &.isMobile {
        // 移动端样式
        .selectItem {
            width: 36vmin !important;
        }
        .canvasRight {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            .buttons {
                width: 60vmin !important;
            }
            .submit {
                width: 60vmin !important;
                font-size: 2.5vmin;
            }
        }
        .itemTip {
            font-size: 2vmin !important;
        }
        .orderItem {
            margin: 0;
            flex-direction: column;
            .left {
                height: auto !important;
                width: 80vmin !important;
            }
        }
        .orderFrom {
            margin: 0 auto;
        }
        .titleBar {
            font-size: 24px !important;
            margin: 3vmin auto !important;
            width: max-content;
            .text {
                font-size: 18px !important;
            }
        }
        
    }

    .header {
        border-bottom: 1px solid #D7D7D7;
        height: 7vmin;
        font-size: 1.6vmin;
        color: #2C2D2E;
        position: sticky;
        top: 0;
        background: #fff;
        z-index: 99;
        .language {
            margin-right: 2vmin;
            font-size: 1.6vmin;
            font-weight: 400;
            cursor: pointer;
            position: relative;
            .text {
                // 悬浮增加下划线
                &:hover {
                    &::after {
                        content: '';
                        position: absolute;
                        bottom: -0.5vmin;
                        left: 0;
                        width: 100%;
                        height: 1px;
                        background-color: #2C2D2E;
                    }
                }
            }
            .downIcon {
                width: 2vmin;
            }
        }
        .logo {
            width: 16vmin;
            margin-right: 12vmin;
        }
        .link {
            margin-right: 4vmin;
            position: relative;
            cursor: pointer;
            transition: all 1s;
            // 悬浮底部横线
            &:hover {
                color: #000;
                &::after {
                    content: '';
                    position: absolute;
                    bottom: -0.5vmin;
                    left: 0;
                    width: 100%;
                    height: 1px;
                    background-color: #2C2D2E;
                }
            }
        }
        .icon {
            width: 2vmin;
            margin-right: 4vmin;
            cursor: pointer;
        }
    }

    .titleBar {
        margin: 3vmin 0 3vmin 16vw;
        font-size: 4vmin;
        color: $theme1Color;
        .title {
            font-weight: bold;
        }
        .text {
            font-size: 1.5vmin;
            margin-top: 2vmin;
        }
        position: relative;
        .setting {
            position: absolute;
            right: 5vmin;
            top: 0;
            &:hover {
                // 旋转
                animation: rotate 1s linear infinite;
                @keyframes rotate {
                    from {
                        transform: rotate(0deg);
                    }
                    to {
                        transform: rotate(360deg);
                    }
                }
            }
        }
    }

    .orderItem {
        margin: 3vmin 0 0 16vw;
        .left {
            width: 50vmin;
            height: 40vmin;
            img {
                width: 100%;
                height: 100%;
                object-fit: contain;
            }
        }
        .right {
            border-bottom: 1px #D2D2D7 solid;
            padding-bottom: 5vmin;
            color: $theme1Color;
            .itemTitle {
                font-size: 2vmin;
                margin-right: 4vmin;
                min-width: 14vmin;
                font-weight: 600;
                font-size: 3vmin;
            }
            .selectItem {
                border-radius: 10px;
                border: 2px solid #86868B;
                font-size: 2vmin;
                padding: 1.5vmin 0;
                width: 20vmin;
                border-radius: 0.5vmin;
                margin: 2vmin 2vmin 0 0;
                &.delete {
                    border: 2px solid #E3001B;
                    color: #E3001B;
                    cursor: pointer;
                }
            }
        }
    }

    .orderFrom {
        margin-left: calc(16vw + 50vmin);
        border-bottom: 1px #D2D2D7 solid;
        width: max-content;
        padding-bottom: 2vmin;
        color: $theme1Color;
        .title {
            margin-top: 3vmin;
            font-weight: 600;
            font-size: 3vmin;
        }
        .inputBox {
            border-radius: 10px;
            font-size: 2vmin;
            padding: 1.5vmin 0;
            border-radius: 0.5vmin;
            margin-top: 3vmin;
            width: max-content;
            input {
                color: #000;
                width: 50vmin;
                // placeholder颜色
                &::placeholder {
                    color: #D4D4D4;
                }
            }
        }
    }

    .styleList {
        padding-left: 16vw;
        font-size: 2vmin;
        .styleListItem {
            font-size: 2vmin;
            border-radius: 0.5vmin;
            border: 2px solid #86868B;
            color: $theme1Color;
            height: 5vmin;
            cursor: pointer;
            padding: 0 1vmin;
            &.active {
                border: 2px solid #0071E3;
            }
        }
    }

    .mainContainer {
        width: 100%;
        position: relative;
        &.isMobile {
            flex-direction: column;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .canvasWrapper {
            width: 80vmin;
            height: 80vmin;
            position: sticky;
            top: 0;
            &.isMobile {
                position: relative;
            }
        }
    }

    .canvasRight {
        color: #000;
        // flex: 1;
        .title {
            font-size: 3vmin;
            margin-bottom: 3vmin;
        }
        .text {
            font-size: 2vmin;
            margin-bottom: 3vmin;
        }
        .item {
            margin-bottom: 4vmin;
            color: $theme1Color;
            .itemTitle {
                font-size: 2vmin;
                margin-right: 4vmin;
                min-width: 14vmin;
                font-weight: 600;
                font-size: 3vmin;
            }

            .itemTip {
                font-size: 1.5vmin;
                color: #999999;
                margin-top: 2vmin;
            }

            .selectItem {
                border-radius: 10px;
                border: 2px solid #86868B;
                font-size: 2vmin;
                padding: 1.5vmin 0;
                width: 20vmin;
                border-radius: 0.5vmin;
                margin: 2vmin 2vmin 0 0;
                cursor: pointer;
                &.select {
                    border: 2px solid #0071E3;
                    color: #0071E3;
                }
            }
        }
        .buttons {
            width: 45vmin;
            font-size: 2vmin;
            .button {
                flex: 1;
                height: 5vmin;
                color: #0079fe;
                border: 1px solid #0079fe;
                border-radius: 0.5vmin;
                &.delete {
                    color: #ef4444; 
                    border: 1px solid #ef4444;
                }
                cursor: pointer;
            }
        }
        .submit {
            color: #fff;
            background: #0079fe;
            border-radius: 0.5vmin;
            width: 45vmin;
            height: 5vmin;
            cursor: pointer;
            font-size: 2vmin;
        }
    }
    
    .controls-hint {
        position: absolute;
        bottom: 20px;
        left: 50%;
        transform: translateX(-50%);
        padding: 8px 16px;
        border-radius: 20px;
        font-size: 14px;
        pointer-events: none;
        z-index: 10;
        color: #121212;
        // 添加一点过渡效果
        transition: opacity 0.3s ease;
        // 背景模糊效果
        backdrop-filter: blur(4px); 
        background: rgba(0, 0, 0, 0.1);
    }
}

.footer {
    margin: 0 auto ;
    padding: 10vmin 0;
    .button {
        padding: 2vmin 0;
        font-size: 2vmin;
        color: #FFFFFF;
        background: #0071E3;
        border-radius: 0.5vmin;
        width: 30vmin;
        transition: all 0.3s;
        cursor: pointer;
        &:hover {
            letter-spacing: 1vmin;
            opacity: 0.8;
        }
    }
}

.settingModal {
    .itemTitle {
        font-weight: 600;
        font-size: 2vmin;
        color: #1D1D1F;
    }
    .selectItem {
        border-radius: 10px;
        border: 2px solid #86868B;
        font-size: 2vmin;
        color: #1D1D1F;
        padding: 1.5vmin;
        border-radius: 0.5vmin;
        margin: 2vmin 2vmin 2vmin 0;
        cursor: pointer;
        &.select {
            border: 2px solid #0071E3;
            color: #0071E3;
        }
    }
    .selectColor {
        width: 5vmin;
        height: 5vmin;
        border-radius: 50%;
        margin-right: 2vmin;
        margin: 2vmin 2vmin 2vmin 0;
        border: 2px solid #86868B;
    }
}

.languageButton {
    padding: 1vmin 2vmin 1vmin 4vmin;
    font-size: 1.5vmin;
    font-weight: 400;
    position: relative;
    .select {
        position: absolute;
        left: 1vmin;
        width: 2vmin;
    }
}
.ant-popover-inner {
    border: 1px solid rgba(0,0,0,.05);
    box-shadow: 0 2px 8px rgba(0,0,0,.15);
}

</style>